package rma.flp;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.annotation.PostConstruct;
import javax.cache.Cache;
import javax.jdo.JDOException;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.Query;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.orm.jdo.JdoCallback;
import org.springframework.orm.jdo.JdoTemplate;
import org.springframework.stereotype.Repository;

/**
 * JDO repository implementation with programmatic caching.
 * 
 * Uses the Google data store and memcache services. 
 * 
 * Caches both the Picture objects (by id) and which pictures
 * belong to which user (by owner email).
 */
@Repository
@SuppressWarnings("unchecked")
public class JdoPictureRepository implements PictureRepository {

	JdoTemplate jdoTemplate;
	Cache pictureCache;
	Cache ownerCache;
	
	@Autowired
	public JdoPictureRepository(PersistenceManagerFactory pmf,
								@Qualifier("pictureCacheHolder") CacheHolder pictureCacheHolder,
								@Qualifier("ownerCacheHolder") CacheHolder ownerCacheHolder) {
		this.jdoTemplate = new JdoTemplate(pmf);
		this.pictureCache = pictureCacheHolder.getCache();
		this.ownerCache = ownerCacheHolder.getCache();
	}
	
	@SuppressWarnings("unused")
	@PostConstruct
	private void fillCache() {
		pictureCache.clear();
		ownerCache.clear();
		jdoTemplate.executeFind(new JdoCallback() {
			public Object doInJdo(PersistenceManager pm) throws JDOException {
				Query query = pm.newQuery(Picture.class);
				Collection<Picture> result = (Collection<Picture>) query.execute();
				for (Picture picture : result) {
					addToPictureCache(picture);
					addToOwnerCache(picture);
				}
				return null;
			}
		});		
	}

	public Picture load(final Long id) {
		return (Picture) pictureCache.get(id);
	}

	public void store(Picture picture) {
		jdoTemplate.makePersistent(picture);
		addToPictureCache(picture);
		addToOwnerCache(picture);
	}
	
	public List<Picture> findByOwnerEmail(String ownerEmail) {
		Set<Long> ids = (Set<Long>) ownerCache.get(ownerEmail);
		if (ids == null) {
			return Collections.emptyList();
		}
		return new ArrayList<Picture>(pictureCache.getAll(ids).values());
	}

	public void delete(final Long id) {
		jdoTemplate.execute(new JdoCallback() {
			public Object doInJdo(PersistenceManager pm) throws JDOException {
				Picture picture = pm.getObjectById(Picture.class, id);
				removeFromPictureCache(id);
				removeFromOwnerCache(picture);
				pm.deletePersistent(picture);
				return null;
			}
		});
	}

	private void addToOwnerCache(Picture picture) {
		String ownerEmail = picture.getOwnerEmail();
		Set<Long> ids = (Set<Long>) ownerCache.get(ownerEmail);
		if (ids == null) {
			ids = new HashSet<Long>();
		}
		// Adding ids to a Set that's a value in the cache map does not work,
		// it has to be added and overwritten.
		ids.add(picture.getId());
		ownerCache.put(ownerEmail, ids);
	}

	private void removeFromOwnerCache(Picture picture) {
		Set<Long> ids = (Set<Long>) ownerCache.get(picture.getOwnerEmail());
		if (ids != null) {
			ids.remove(picture.getId());
		}
	}

	private void addToPictureCache(Picture picture) {
		pictureCache.put(picture.getId(), picture);
	}

	private void removeFromPictureCache(final Long id) {
		pictureCache.remove(id);
	}

}
